/*
 * Copyright (c) 2010, VeRSI Consortium
 *   (Victorian eResearch Strategic Initiative, Australia)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the VeRSI, the VeRSI Consortium members, nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package versi.vitro.oaiserviceprovider.service.jena.impl;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.FactoryBean;

import versi.vitro.oaiserviceprovider.service.jena.api.JenaConnManager;

import com.hp.hpl.jena.db.DBConnection;
import com.hp.hpl.jena.db.IDBConnection;
import com.hp.hpl.jena.db.impl.IRDBDriver;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;

/**
 * Service implementing {@link JenaConnManager}.
 */
public class JenaConnManagerImpl implements JenaConnManager, FactoryBean {

    /** log4j instance. */
    private static final Logger LOGGER = Logger.getLogger(JenaConnManagerImpl.class);

    private final static String DATABASE_TYPE = "MySQL";

    /** Jena DB connection. */
    private IDBConnection dbConn = null;

    /** URL of JENA database. */
    private String jenaDbUrl;

    /** Login Username to JENA database. */
    private String username;

    /** Login Password to JENA database. */
    private String password;

    /**
     * Default Constructor.
     */
    public JenaConnManagerImpl() {

    };

    /**
     * Constructor.
     * 
     * @param jenaDBurl
     *            URL of JENA database.
     * @param username
     *            Login Username to JENA database.
     * @param password
     *            Login Password to JENA database.
     * 
     */
    public JenaConnManagerImpl(final String jenaDBurl, final String username, final String password) {
	this.jenaDbUrl = jenaDBurl;
	this.username = username;
	this.password = password;
    }

    /** {@inheritDoc} */
    public ModelMaker getModelMaker() {
	connect();
	return ModelFactory.createModelRDBMaker(dbConn);
    }

    /** {@inheritDoc} */
    public JenaConnManager connect() {
	try {
	    Class.forName("com.mysql.jdbc.Driver");
	    dbConn = new DBConnection(jenaDbUrl, username, password, DATABASE_TYPE);
	    
	    // RR: We make the next 2 calls to force a database connection.
	    // Otherwise the integration tests will fail.
	    @SuppressWarnings("unused")
	    Connection connection = dbConn.getConnection();
	    @SuppressWarnings("unused")
	    IRDBDriver driver = dbConn.getDriver();
	    
	    // The default txIsolation is 4 = TRANSACTION_REPEATABLE_READ
	    // which is fine.
	    // Except that if the binary logging is enabled in my.cnf the
	    // model creation throws an exception to the effect
	    // that this is incompatible with TRANSACTION_READ_COMMITTED
	    // which does not make sense.
	    // Changing the isolation level here appears to have no effect.
	    // So disable the #log-bin=mysql-bin setting in my.cnf
	    
	    LOGGER.info("Connecting to DB at " + jenaDbUrl);
	} catch (RuntimeException rtException) {
	    String message = "Jena database connection could not be made. Runtime exception";
	    LOGGER.fatal(message, rtException);
	    throw new RuntimeException(message, rtException);
	} catch (ClassNotFoundException cnfException) {
	    String message = "Jena database connection could not be made. Database driver not located. ";
	    LOGGER.fatal(message, cnfException);
	    throw new RuntimeException(message, cnfException);
	} catch (SQLException sqlException) {
	    String message = "Jena database connection could not be made. SQL Exception. ";
	    LOGGER.fatal(message, sqlException);
	    throw new RuntimeException(message, sqlException);
	}
	if (LOGGER.isInfoEnabled()) {
	    LOGGER.info("Successful login to VITRO Jena repository");
	}
	return this;
    }

    /** {@inheritDoc} */
    public void disconnect() {

	try {
	    dbConn.close();
	    if (LOGGER.isInfoEnabled()) {
		LOGGER.info("Successful logout from VITRO repository");
	    }
	} catch (SQLException e) {
	    throw new RuntimeException("Database disconnect failed. ", e);
	}
    }

    /**
     * @exception java.lang.Exception
     *                Thrown from getting model maker.
     */
    public Object getObject() throws Exception {
	return getModelMaker();
    }

    @SuppressWarnings("unchecked")
    public Class getObjectType() {
	return ModelMaker.class;
    }

    public boolean isSingleton() {
	return true;
    }

}
